﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
//http://roguebasin.roguelikedevelopment.org/index.php?title=Cellular_Automata_Method_for_Generating_Random_Cave-Like_Levels

namespace SkyBase
{
    class LevelGen
    {
        Random r;

        const int TILE_ENEMY = -5;
        const int TILE_HP = -3;
        const int TILE_ITEM = -2;
        const int TILE_STAIRS = -1;
        const int TILE_FLOOR = 0;
        const int TILE_WALL = 1;
        const int TILE_SKY = 2;
        const int TILE_CLIFF = 3;

        struct generation_params {
            public int r1_cutoff, r2_cutoff;
            public int reps; }

        int[,] grid;
        int[,] grid2;

        int fillprob = 40;
        int size_x = 64;
        int size_y = 20;
        int cliffProb = 35;
        generation_params param;

        generation_params[] param_set;
        int generations = 4;

        //public string map;

        public LevelGen(Random rand)
        {
            r = rand;
        }

        /// <summary>
        /// Generate a random level
        /// </summary>
        /// <param name="width">size_x, width of level</param>
        /// <param name="height">size_y, height of level</param>
        /// <param name="fillProb">Probability of filling</param>
        /// <param name="r1">r1_cutoff</param>
        /// <param name="r2">r1_cutoff</param>
        /// <param name="generationCt"></param>
        /// <param name="reps">More reps = smoother edges</param>
        public int[,] CreateLevel(int width, int height, int fillProb, int r1, int r2, int generationCt, int reps)
        {
            size_x = width;
            size_y = height;
            fillprob = fillProb;
            //r1_cutoff = r1;
            //r2_cutoff = r2;

            param_set = new generation_params[generationCt];

            for (int n = 0; n< generationCt; n++)
            {
                generation_params p = new generation_params();
                p.r1_cutoff = r1;
                p.r2_cutoff = r2;
                p.reps = reps;
                param_set[n] = p;
            }

            initmap();

            for (int n = 0; n < generationCt; n++)
            {
                param = param_set[n];
                for (int i = 0; i < param.reps; i++)
                    generation();
            }

            //removing extra wall tiles
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                {
                    if (y == 0)//top row
                    {
                        if (x == 0)//0,0
                        {
                            if (grid[x + 1, y] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR && grid[x + 1, y + 1] > TILE_FLOOR)
                                grid[x, y] = TILE_SKY;
                        }
                        else if (x == size_x - 1)//size_x - 1, 0
                        {
                            if (grid[x - 1, y] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR && grid[x - 1, y + 1] > TILE_FLOOR)
                                grid[x, y] = TILE_SKY;
                        }
                        else if (grid[x - 1, y] > TILE_FLOOR && grid[x + 1, y] > TILE_FLOOR && grid[x - 1, y + 1] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR && grid[x + 1, y + 1] > TILE_FLOOR)
                            grid[x, y] = TILE_SKY;
                    }

                    else if (y == size_y - 1)//bottom row
                    {
                        if (x == 0)
                        {
                            if (grid[x, y - 1] > TILE_FLOOR && grid[x + 1, y] > TILE_FLOOR && grid[x + 1, y - 1] > TILE_FLOOR)
                                grid[x, y] = TILE_SKY;
                        }
                        else if (x == size_x - 1)//size_x - 1, size_y - 1
                        {
                            if (grid[x, y - 1] > TILE_FLOOR && grid[x - 1, y] > TILE_FLOOR && grid[x - 1, y - 1] > TILE_FLOOR)
                                grid[x, y] = TILE_SKY;
                        }
                        else if (grid[x - 1, y] > TILE_FLOOR && grid[x + 1, y] > TILE_FLOOR && grid[x, y - 1] > TILE_FLOOR && grid[x - 1, y - 1] > TILE_FLOOR && grid[x + 1, y - 1] > TILE_FLOOR)
                            grid[x, y] = TILE_SKY;
                    }

                    else if (x == 0)//left edge
                    {
                        if (grid[x, y - 1] > TILE_FLOOR && grid[x + 1, y - 1] > TILE_FLOOR && grid[x + 1, y] > TILE_FLOOR && grid[x + 1, y + 1] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR)
                            grid[x, y] = TILE_SKY;
                    }
                    else if (x == size_x - 1)//right edge
                    {
                        if (grid[x, y - 1] > TILE_FLOOR && grid[x - 1, y - 1] > TILE_FLOOR && grid[x - 1, y] > TILE_FLOOR && grid[x - 1, y + 1] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR)
                            grid[x, y] = TILE_SKY;
                    }
                    //center
                    else if (grid[x, y - 1] > TILE_FLOOR && grid[x + 1, y - 1] > TILE_FLOOR && grid[x + 1, y] > TILE_FLOOR && grid[x + 1, y + 1] > TILE_FLOOR && grid[x, y + 1] > TILE_FLOOR && grid[x - 1, y + 1] > TILE_FLOOR && grid[x - 1, y] > TILE_FLOOR)
                        grid[x, y] = TILE_SKY;
                }
            }/**/

            makeCliff();

            //printfunc();
            //map = getMap();
            //printmap();
            return grid;
        }



        public int randPick()
        {
            if (r.Next(0, 100) < fillprob)
                return TILE_WALL;
            else
                return TILE_FLOOR;
        }

        public void initmap()
        {
            grid = new int[size_x,size_y];
            grid2 = new int[size_x, size_y];

            for (int y = 1; y < size_y - 1; y++)
            {
                for (int x = 1; x < size_x - 1; x++)
                    grid[x, y] = randPick();
            }
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                    grid2[x, y] = TILE_WALL;
            }
            for (int y = 0; y < size_y; y++)
                grid[0, y] = grid[size_x - 1, y] = TILE_WALL;
            for (int x = 0; x < size_x; x++)
                grid[x, 0] = grid[x, size_y - 1] = TILE_WALL;
        }

        public void generation()
        {
            int adjcount_r1 = 0;
            int adjcount_r2 = 0;
            for (int y = 1; y < size_y - 1; y++)
            {
                for (int x = 1; x < size_x - 1; x++)
                {
                    adjcount_r1 = 0;
                    adjcount_r2 = 0;

                    for (int i = -1; i <= 1; i++)
                    {
                        for (int j = -1; j<=1; j++)
                        {
                            if (grid[x + j, y + i] != TILE_FLOOR)
                                adjcount_r1++;
                        }
                    }
                    for (int i = y - 2; i <= y + 2; i++)
                    {
                        for (int j = x - 2; j <= x + 2; j++)
                        {
                            if (Math.Abs(i - y) == 2 && Math.Abs(j - x) == 2)
                                continue;
                            if (i < 0 || j < 0 || i >= size_y || j >= size_x)
                                continue;
                            if (grid[j, i] != TILE_FLOOR)
                                adjcount_r2++;
                        }
                    }
                    if (adjcount_r1 >= param.r1_cutoff || adjcount_r2 <= param.r2_cutoff)
                        grid2[x, y] = TILE_WALL;
                    else
                    {
                        grid2[x, y] = TILE_FLOOR;
                    }
                }
            }
            for (int y = 1; y < size_y - 1; y++)
            {
                for (int x = 1; x < size_x - 1; x++)
                    grid[x, y] = grid2[x, y];
            }

            
            spawnStuff();
        }

        public void printfunc()
        {
            Console.WriteLine("W[0](p) = rand[0,100) < " + fillprob);
            for (int i = 0; i < generations; i++)
            {
                Console.Write("Repeat "+param_set[i].reps+": W'(p) = R[1](p) >= "+param_set[i].r1_cutoff);
                if (param_set[i].r2_cutoff >= 0)
                    Console.WriteLine(" || R[2](p) <= " + param_set[i].r2_cutoff);
                else
                    Console.WriteLine();
            }
        }

        public void printmap()
        {
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                {
                    if (grid[x, y] == TILE_WALL)
                        Console.Write("#");
                    else if (grid[x, y] == TILE_FLOOR)
                        Console.Write(" ");
                    else if (grid[x, y] == TILE_SKY)
                        Console.Write("_");
                    else if (grid[x, y] == TILE_STAIRS)
                        Console.Write("<");
                    else if (grid[x, y] == TILE_ITEM)
                        Console.Write("o");
                    else// if (grid[x,y] == TILE_ENEMY)
                        Console.Write("e");
                }
                Console.WriteLine();
            }
        }

        public string getMap()
        {
            string s = "";
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                {
                    if (grid[x, y] == TILE_WALL)
                        s += "#";
                    else if (grid[x, y] == TILE_FLOOR)
                        s += " ";
                    else if (grid[x, y] == TILE_SKY)
                        s += "_";
                    else if (grid[x, y] == TILE_STAIRS)
                        s += "<";
                    else if (grid[x, y] == TILE_ITEM)
                        s += "o";
                    else// if (grid[x,y] == TILE_ENEMY)
                        s += "e";
                }
                s += '\n';
            }
            return s;
        }

        public void spawnStuff()
        {
            int n = 1;
            while (n > 0)
            {
                int x = r.Next(1, size_x - 1);
                int y = r.Next(1, size_y - 1);
                if (grid[x, y] == TILE_FLOOR)
                {
                    grid[x, y] = TILE_STAIRS;
                    n--;
                }
            }
            int numItems = r.Next(0, 7);
            while (numItems > 0)
            {
                int x = r.Next(1, size_x - 1);
                int y = r.Next(1, size_y - 1);
                if (grid[x, y] == TILE_FLOOR)
                {
                    grid[x, y] = TILE_ITEM;
                    numItems--;
                }
            }

            int numEnemy = r.Next(10, 30);
            while (numEnemy > 0)
            {
                int x = r.Next(1, size_x - 1);
                int y = r.Next(1, size_y - 1);
                if (grid[x, y] == TILE_FLOOR)
                {
                    grid[x, y] = TILE_ENEMY;
                    numEnemy--;
                }
            }

            n = 1;
            while (n > 0)
            {
                int x = r.Next(1, size_x - 1);
                int y = r.Next(1, size_y - 1);
                if (grid[x, y] == TILE_FLOOR)
                {
                    grid[x, y] = TILE_HP;
                    n--;
                }
            }
        }

        public void makeCliff()
        {
            int num;
            for (int y = 0; y < size_y; y++)
            {
                for (int x = 0; x < size_x; x++)
                {
                    num = r.Next(0, 100);
                    if (grid[x,y] == TILE_WALL && num < cliffProb)
                        grid[x, y] = TILE_CLIFF;
                }
            }
        }
    }
}